<?php
/**
 * ActiveRecord SQL语句处理和数据获取类
 *
 * @author Mo yi <root@imoi.cn>
 * @link   http://www.imoi.cn
 * @copyright Copyright &copy; 2010-2012 DuoLam Software LLC
 * @license http://www.imoi.cn/license/
 * @version $Id: dpSchema.php 2799 2012-04-10 09:31:13Z Mo.yi $
 * @package db.schema
 * @since 1.0
 */

class dpActiveFinder extends dpComponent
{
	/**
	 * 数据库连接
	 */
	protected $db;

	/**
	 * 模型
	 */
	protected $model;

	/**
	 * 架构句柄
	 */
	protected $s;

	/**
	 * SQL存储器
	 */
	protected $sql;

	/**
	 * 元信息
	 */
	protected $meta;

	/**
	 * 主键名
	 */
	protected $pkName;

	/**
	 * 处理进程
	 */
	public $process;

	/**
	 * 构造方法
	 * @param object $model  当前数据模型
	 * @param object $s      数据架构句柄
	 */
	public function __construct(&$model,$s)
	{
		$this->model = $model;
		$this->db    = &$model->db;
		$this->s     = $s;
		$this->meta  = $this->s->getColumn($this->model->tableName());

		if ($this->process === null) $this->process = new dpActiveRecordProcess($model,$s);

		$this->pkName = $this->model->primaryKey();
		unset($model,$s);
	}

	/**
	 * find类操作数据获取方法
	 * @param string $pk 主键值
	 * @param string || array $conditions 查询条件
	 * @param array  $params  参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getFind($pk = '', $conditions = '', $params = array())
	{
		$this->sql = $this->process->getFindString($this->pkName, $pk, $conditions);
		$this->sql = $this->parseSql();
		$record    = $this->db->find($this->sql, $params);
		$this->setAttribute($record);
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($this->sql),
			'record' => $record
		);
	}

	/**
	 * finds类操作数据获取方法
	 * @param string $pk 主键值
	 * @param string || array $conditions 查询条件
	 * @param array  $params  参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getFinds($pk = '', $conditions = '', $params = array())
	{
		$this->sql = $this->process->getFindString($this->pkName, $pk, $conditions);
		$this->sql = $this->parseSql();
		$record    = $this->db->finds($this->sql, $params);
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($this->sql),
			'record' => $record
		);
	}

	/**
	 * find类按要查询的字段数据获取方法
	 * @param string || array $attr 要查询的字段
	 * @param string || array $conditions 查询条件
	 * @param array $params 参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getAttrFind($attr, $conditions = '', $params = array())
	{
		$this->sql = $this->process->getFindAttrString($attr, $conditions);
		$this->sql = $this->parseSql();
		$record    = $this->db->find($this->sql, $params);
		$this->setAttribute($record);
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($this->sql),
			'record' => $record
		);
	}

	/**
	 * finds类按要查询的字段数据获取方法
	 * @param string || array $attr 要查询的字段
	 * @param string || array $conditions 查询条件
	 * @param array $params 参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getAttrFinds($attr, $conditions = '', $params = array())
	{
		$this->sql = $this->process->getFindAttrString($attr, $conditions);
		$this->sql = $this->parseSql();
		$record    = $this->db->finds($this->sql, $params);
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($this->sql),
			'record' => $record
		);
	}

	/**
	 * find按SQL查询的操作方法
	 * @param string $sql 原生SQL语句
	 * @param array $params 参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getSqlFind($sql, $params = array())
	{
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($sql),
			'record' => $this->db->find($sql, $params)
		);
	}

	/**
	 * finds按SQL查询的操作方法
	 * @param string $sql 原生SQL语句
	 * @param array $params 参数绑定
	 * @return array SQL语句和结果集
	 */
	public function getSqlFinds($sql, $params = array())
	{
		return array(
			'sql'    => $this->db->current->getDealTablePrefix($sql),
			'record' => $this->db->finds($sql, $params)
		);
	}

	/**
	 * 获取当前结果集的行数
	 * @param string $sql SQL语句
	 * @return int 结果集的行数
	 */
	public function getCount($sql = '')
	{
		return $this->db->count($sql);
	}

	/**
	 * 获取delete操作的结果
	 * @param int $pk  主键值
	 * @param string || array $conditions 删除条件
	 * @param array $params  参数绑定
	 * @return 返回影响的行数
	 */
	public function getDelete($pk = '', $conditions = '', $params = array())
	{
		$c = $this->process->getConditionsString($this->pkName, $pk, $conditions);

		if ($this->relationEvent('delete', $pk, $c)) {
			return $this->db->delete($this->model->tableName(), $c, $params);
		}

		return false;
		
	}

	/**
	 * 获取update操作的结果
	 * @param int $pk  主键值
	 * @param array $attributes 属性字段
	 * @param string || array $conditions 删除条件
	 * @param array $params  参数绑定
	 * @return 返回影响的行数
	 */
	public function getUpdate($pk = '', $attributes, $conditions = '', $params = array())
	{
		$c = $this->process->getConditionsString($this->pkName, $pk, $conditions);
		if ($this->relationEvent('update', $pk, $c, $attributes['attrs'])) {
			return $this->db->update($this->model->tableName(), $attributes['attr'], $c, $params);
		}
		return false;
	}

	/**
	 * 获取insert操作后的结果
	 * @param array $attributes 属性字段
	 * @param 返回insert_id
	 */
	public function getInsert($attributes)
	{
		if ($this->s->getPk()) {
			$pk = $this->s->getPk();
			$meta   = $this->model->getMeta();

			foreach ($meta as $v) {
				if ($v['name'] == $pk && isset($v['auto']) && $v['auto'] != false)
					unset($attributes[$this->s->getPk()]);
			}

		}
		$pkId = $this->db->insert($this->model->tableName(), $attributes['attr']);

		if ($pkId) {
			$this->relationEvent('insert', $pkId, null, $attributes['attrs']);
		}

		return $pkId;
	}

	/**
	 * 设置或改变当前数据模型的属性值
	 * @param array $record 结果集
	 */
	protected function setAttribute($record)
	{	
		foreach ($this->meta as $v) {
			if (isset($record[$v['name']]))
				$this->model->setAttribute($v['name'],$record[$v['name']]);
		}
	}

	/**
	 * 解析SQL语句，对SQL进行处理
	 * @return string $sql SQL语句
	 */
	public function parseSql()
	{
		$sql = null;
		$sql .= $this->getCheckVar('select');
		$sql .= $this->getCheckVar('from');
		$sql .= $this->getCheckVar('join');
		$sql .= $this->getCheckVar('where');
		$sql .= $this->getCheckVar('group');
		$sql .= $this->getCheckVar('having');
		$sql .= $this->getCheckVar('order');
		$sql .= $this->getCheckVar('limit');
		return $sql;
	}

	/**
	 * 对SQL语句处理并加上\n转义字符
	 * @param string $val 类型名称
	 * @return string $sql 返回某一段的SQL语句
	 */
	public function getCheckVar($var)
	{
		return isset($this->sql[$var]) ? "\n" . $this->sql[$var] : null;
	}

	/** 
	 * 执行关联事件
	 * @param string $event 事件名
	 * @param int    $pk    primary key
	 */
	protected function relationEvent($event, $pk = null, $conditions = null, $data = null)
	{
		$name = $event . 'Relation';
		return $this->$name($pk, $conditions, $data);
	}

	/**
	 * 插入关联操作
	 */
	protected function insertRelation($pk, $conditions = null, $data)
	{
		$models = $this->process->getRelation();

		if ($models === false) return true;
		foreach ($models as $k=>$m) {
			$fid  = $m['fid'];
			$meta = $m['model']->getMeta();
			$isauto = false;
			foreach ($meta as $k => $mt) {
				if (($m['fid'] == $mt['name']) && isset($mt['pk'])) {
					$isauto = true;
					break;
				}
			}
			if (!$isauto) {
				$data[$m['fid']] = $pk;
			}
			$m['model']->insert($data);
		}
	}

	/**
	 * 删除关联操作
	 */
	protected function deleteRelation($pk, $conditions = null)
	{
		$models = $this->process->getRelation();
		if ($models === false) return true;

		foreach ($models as $k => $m) {
			$m['model']->deletes($m['fid'] . ' = ' . $pk);
		}

		return true;
	}

	/**
	 * 更新关联操作
	 */
	protected function updateRelation($pk, $conditions = null, $data)
	{	
		$models = $this->process->getRelation();

		if ($models === false) return true;

		foreach ($models as $k => $m) {
			$m['model']->updates($data, $m['fid'] . ' = ' . $pk);
		}

		return true;
	}
	
}


/**
 * ActiveRecord 外部全局操作处理类
 */
class dpActiveRecordProcess
{
	/**
	 * 当前数据模型
	 */
	protected $model;

	/**
	 * 数据架构句柄
	 */
	protected $s;

	/**
	 * 默认关联查询类型
	 */
	protected $joinType = 'outer';

	/**
	 * 关联模型寄存器
	 */
	protected $relaModel;

	/**
	 * 构造方法
	 * @param object $model 当前模型
	 * @param object $s 	数据架构句柄
	 */
	public function __construct(&$model,&$s)
	{
		$this->model = $model;
		$this->s     = $s;
	}

	/**
	 * 解析查询范围
	 * @param array $names  查询范围配置数组
	 * @param array $params 查询范围参数 
	 * @param array 返回解析过的配置数组
	 */
	public function parseScopes($names, $params)
	{
		foreach ($names as $name => $v) {
			if ($this->getScopesType($name) !== null) {
				if ($name == 'condition' or $name == 'having')
					$sql[$name] = $this->s->parseConditions($this->parseScopesParams($params, $v));
				else
					$sql[$this->getScopesType($name)] = $this->parseScopesParams($params, $v);
			}
		}

		return $sql;
	}

	/**
	 * 解析查询范围代进来的参数
	 * @param array  $params    参数列表
	 * @param string $scopesSql 查询范围SQL语句
	 * @return string 返回解析完毕的SQL语句
	 */
	protected function parseScopesParams($params, $scopesSql)
	{
		if (is_array($params)) {
			foreach ($params as $k => $v) {
				$scopesSql = preg_replace('/\{'.$k.'\}/', $v, $scopesSql);
			}
		}

		return $scopesSql;
	}

	/**
	 * 获取查询范围的类型
	 * @param string $key 类型名称
	 * @return string 类型
	 */
	public function getScopesType($key)
	{
		$type = array(
			'condition' => 'condition',
			'order'      => 'order',
			'group'		 => 'group',
			'having'     => 'having',
			'limit'      => 'limit',
		);
		return isset($type[$key]) ? $type[$key] : null;
	}

	/**
	 * 获取查询范围处理过的SQL语句
	 * @param array $sql SQL语句数组
	 */
	public function getScopesString(&$sql)
	{
		$scopes = $this->model->scopesProcess;

		if ($scopes === array())
			return null;
		else {
			$sql['where'] .= (!empty($scopes['condition']) || ($sql['where'] == ' WHERE 1=1'))? 
							 empty($scopes['condition']) ? null : ' AND ' . $this->s->parseConditions($scopes['condition']) :
							 null;
			$sql['order']  = $this->s->getOrderString(empty($scopes['order'])   ? '' : $scopes['order']);
			$sql['group']  = $this->s->getGroupString(empty($scopes['group'])   ? '' : $scopes['group']);
			$sql['having'] = $this->s->getHavingString(empty($scopes['having']) ? '' : $scopes['having']);
			$sql['limit']  = $this->s->getLimitString(empty($scopes['limit'])   ? '' : $scopes['limit']);
			$this->model->scopesProcess = array();
		}
	}

	/**
	 * 解析关联模型，判断当前查询是否进行了关联查询
	 * @return boolean
	 */
	public function parseRelations()
	{
		if (empty($this->model->with))
			return false;
		else {
			foreach ($this->model->with as $k => $v) {

				if ((empty($v[0]) || empty($v[1])))
					dpException::error('关联模型参数给予不完整，模型:'.get_class($this->model).'，关联名:'.$k);

				$m = $v[0];
				$this->relaModel[$k]['model'] = new $m;
				$this->relaModel[$k]['model']->setTableAlias($k);
				$this->relaModel[$k]['id']    = $v[1];
				$this->relaModel[$k]['fid']	  = isset($v[2]) ? $v[2] : null;
				$this->relaModel[$k]['attr']  = isset($v[3]) ? $v[3] : null;
				$this->relaModel[$k]['condition'] = !empty($v[4]) ? $v[4] : null;
				$this->relaModel[$k]['join']  = isset($v[5]) ? $v[5] : $this->joinType;
			}

			$this->model->with = null;
			return true;
		}
	}

	/**
	 * 获取关联查询部分的SQL，对SQL进行拼装
	 * @param array $sql SQL语句数组
	 * @return null 在没有关联查询的时候，返回为空
	 */
	public function getRelationString(&$sql)
	{
		$sql['join'] = null;

		if ($this->parseRelations()) {
			foreach ($this->relaModel as $k => $v) {
				if(empty($v['fid'])) $v['fid'] = $v['model']->primaryKey();

				if(empty($v['attr']))
					$sql['select'] .= ', ' . $v['model']->getTableAlias() . '.*';
				else
					$sql['select'] .= ', ' . $this->parseSelectAttr($v['attr'], $v['model']->getTableAlias());

				$sql['join']   .= $this->s->getJoinString(
					array($v['model']->tableName()=>$v['model']->getTableAlias()),
					$v['join'],
					$this->model->getTableAlias() . '.'. $v['id'] . '=' . $v['model']->getTableAlias() . '.' . $v['fid']);

				if (isset($v['condition'])) {
					$sql['where'] .= ' AND ' . $this->s->parseConditions($v['condition']);
				}
			}
		} else
			return '';
	}

	/**
	 * 解析查询属性
	 * @param array || string $attr 字段
	 * @param string $alias 表别名
	 * @return string 解析后的查询字段
	 */
	public function parseSelectAttr($attr,$alias = null)
	{
		if (is_array($attr)) {
			foreach ($attr as $k => $v) {
				if (preg_match('/(\w+\(.*?\))/i', $v)) {
					$at[] = $v.'  ';
				} else {
					$f    = is_integer($k) ? $v : $k . ' AS ' . $v;
					$at[] = empty($alias) ? trim($f) . ', ' : $alias . '.' . trim($f) . ', ';
				}
			}
			return substr(implode('',$at), 0, -2);
		} else {
			return $this->parseSelectAttr(explode(',', $attr), $alias);
		}
	}

	/**
	 * 获取find类操作的SQL字符串
	 * @param string $pkName 主键名
	 * @param string $pkValue 主键值
	 * @param array || string $conditions 查询条件
	 * @return string $sql 查询SQL语句
	 */
	public function getFindString($pkName, $pkValue, $conditions)
	{
		$pkString      = empty($pkValue)  ? null : $this->model->getTableAlias() . '.' . $pkName . '=' . $pkValue;
		$conditions    = empty($pkString) ? $conditions : $pkString . (empty($conditions) ? null : ' AND ' . $conditions);
		$sql['select'] = 'SELECT ' . $this->model->getTableAlias() . '.*';
		$sql['from']   = $this->s->getFromString(array($this->model->tableName()=>$this->model->getTableAlias()));
		$sql['where']  = $this->s->getWhereString($conditions);
		$this->getRelationString($sql);
		$this->getScopesString($sql);
		return $sql;
	}

	/**
	 * 获取find类按查询字段操作的SQL字符串
	 * @param array || string $attr 要查询的字段
	 * @param array || string $conditions 查询条件
	 * @return string $sql 查询SQL语句
	 */
	public function getFindAttrString($attr,$conditions)
	{
		$sql['select'] = 'SELECT ' . $this->parseSelectAttr($attr, $this->model->getTableAlias());
		$sql['from']   = $this->s->getFromString(array($this->model->tableName()=>$this->model->getTableAlias()));
		$sql['where']  = $this->s->getWhereString($conditions);
		$this->getRelationString($sql);
		$this->getScopesString($sql);
		return $sql;
	}

	/**
	 * 获取条件SQL字符串
	 * @param string 主键名
	 * @param int    主键ID
	 * @param array || string  $conditions 条件
	 * @return string 组合后的条件部分语句
	 */
	public function getConditionsString($pkName, $pkValue, $conditions)
	{
		$pkString   = empty($pkValue)  ? null : $pkName . '=' . $pkValue;
		$conditions = empty($pkString) ? $conditions : $pkString . (empty($conditions) ? null : ' AND ' . $this->s->parseConditions($conditions));
		return $conditions;
	}

	/**
	 * 获取当前关联模型寄存器的所有模型
	 * @return array 模型寄存器列表
	 */
	public function getRelation()
	{
		if ($this->parseRelations())
			return $this->relaModel;
		else
			return false;
	}
}
?>